﻿using System;
using System.Data;
using System.Data.SqlClient;
using Microsoft.Synchronization;
using Microsoft.Synchronization.Data;
using Microsoft.Synchronization.Data.SqlServer;
using System.Configuration;
using System.Collections.Generic;
using log4net;
using log4net.Config;
using System.IO;
using System.Text;

namespace DBSynchronizer
{
    class Program
    {
        private static ILog Log = null;

        private static string SyncScope = "SyncScope";

        private static IList<string> TableList = null;

        static void Main(string[] args)
        {
            InitLog();
            Log.Info("Start.");
            if (args != null && args.Length > 0)
            {
                Log.Info("Opration Type:<" + args[0] + ">.");
                // 初始化同步Scope
                if ("-i".Equals(args[0]))
                {
                    Log.Info("ProvisionDB started.");
                    ProvisionDB();
                    Log.Info("ProvisionDB finished.");
                }
                // 数据库同步
                else if ("-s".Equals(args[0]))
                {
                    Log.Info("SyncDB started.");
                    SyncDB();
                    Log.Info("SyncDB finished.");
                }
                else if ("-d".Equals(args[0]))
                {
                    Log.Info("DeprovisionDB started.");
                    DeprovisionDB();
                    Log.Info("DeprovisionDB finished.");
                }
            }
            Log.Info("End.");
        }

        private static void SyncDB()
        {
            SqlConnection clientConn = new SqlConnection(ConfigurationManager.AppSettings["ClientDataSource"]);

            SqlConnection serverConn = new SqlConnection(ConfigurationManager.AppSettings["ServerDataSource"]);
            try
            {
                //创建同步代理（sync orhcestrator）
                SyncOrchestrator syncOrchestrator = new SyncOrchestrator();

                //设置同步代理的本地同步提供程序为local database的Scope
                syncOrchestrator.LocalProvider = new SqlSyncProvider(SyncScope, clientConn);

                //设置同步代理的远程同步提供程序为server database的Scope
                syncOrchestrator.RemoteProvider = new SqlSyncProvider(SyncScope, serverConn);

                //设置同步会话的同步方向为Upload和Download
                syncOrchestrator.Direction = SyncDirectionOrder.UploadAndDownload;

                //侦听本地同步提供程序的ApplyChangeFailed事件
                ((SqlSyncProvider)syncOrchestrator.LocalProvider).ApplyChangeFailed += new EventHandler<DbApplyChangeFailedEventArgs>(Program_ApplyChangeFailed);

                //执行同步过程
                SyncOperationStatistics syncStats = syncOrchestrator.Synchronize();

                //输出统计数据
                Log.Info("--Start Time: " + syncStats.SyncStartTime);
                Log.Info("--Total Changes Uploaded: " + syncStats.UploadChangesTotal);
                Log.Info("--Total Changes Downloaded: " + syncStats.DownloadChangesTotal);
                Log.Info("--Complete Time: " + syncStats.SyncEndTime);
            }
            catch (Exception e)
            {
                Log.Error(e.Message, e);
            }
            finally
            {
                if (clientConn != null)
                    clientConn.Close();
                if (serverConn != null)
                    serverConn.Close();
            }
        }

        private static void DeprovisionDB()
        {
            SqlConnection clientConn = new SqlConnection(ConfigurationManager.AppSettings["ClientDataSource"]);
            SqlConnection serverConn = new SqlConnection(ConfigurationManager.AppSettings["ServerDataSource"]);

            try
            {
                SqlSyncScopeDeprovisioning serverDeprovision = new SqlSyncScopeDeprovisioning(serverConn);
                serverDeprovision.DeprovisionScope(SyncScope);

                SqlSyncScopeDeprovisioning clientDeprovision = new SqlSyncScopeDeprovisioning(clientConn);
                clientDeprovision.DeprovisionScope(SyncScope);
            }
            catch (Exception e)
            {
                Log.Error(e.Message, e);
            }
            finally
            {
                if (clientConn != null)
                {
                    clientConn.Close();
                    clientConn.Dispose();
                }
                if (serverConn != null)
                {
                    serverConn.Close();
                    serverConn.Dispose();
                }
            }
        }

        private static void ProvisionDB()
        {
            SqlConnection clientConn = new SqlConnection(ConfigurationManager.AppSettings["ClientDataSource"]);
            SqlConnection serverConn = new SqlConnection(ConfigurationManager.AppSettings["ServerDataSource"]);

            try
            {
                ProvisionServer(serverConn);
                ProvisioClient(clientConn, serverConn);
            }
            catch (Exception e)
            {
                Log.Error(e.Message, e);
            }
            finally
            {
                if (clientConn != null)
                {
                    clientConn.Close();
                    clientConn.Dispose();
                }
                if (serverConn != null)
                {
                    serverConn.Close();
                    serverConn.Dispose();
                }
            }
        }

        private static void ProvisionServer(SqlConnection serverConn)
        {
            DbSyncScopeDescription scopeDesc = new DbSyncScopeDescription(SyncScope);

            ListTable();

            if (TableList == null || TableList.Count == 0)
                return;

            foreach (string tableName in TableList)
            {
                DbSyncTableDescription schoolTableDesc = SqlSyncDescriptionBuilder.GetDescriptionForTable(tableName, serverConn);
                scopeDesc.Tables.Add(schoolTableDesc);
            }

            //创建一个同步作用域设置对象
            SqlSyncScopeProvisioning serverProvision = new SqlSyncScopeProvisioning(serverConn, scopeDesc);

            //如果表已经存在则略过创建表的步骤
            serverProvision.SetCreateTableDefault(DbSyncCreationOption.Skip);

            //开始设置过程
            serverProvision.Apply();
        }

        private static void ProvisioClient(SqlConnection clientConn, SqlConnection serverConn)
        {
            DbSyncScopeDescription scopeDesc = SqlSyncDescriptionBuilder.GetDescriptionForScope(SyncScope, serverConn);
            SqlSyncScopeProvisioning clientProvision = new SqlSyncScopeProvisioning(clientConn, scopeDesc);

            //开始设置过程
            clientProvision.Apply();
        }

        static void Program_ApplyChangeFailed(object sender, DbApplyChangeFailedEventArgs e)
        {
            //显示冲突类型
            Log.Error(e.Conflict.Type);

            DataTable conflictingServerChange = e.Conflict.LocalChange;
            DataTable conflictingClientChange = e.Conflict.RemoteChange;
            StringBuilder clientRowAsString = new StringBuilder();
            StringBuilder serverRowAsString = new StringBuilder();
            if (conflictingServerChange != null)
            {
                int serverColumnCount = conflictingServerChange.Columns.Count;
                for (int i = 0; i < serverColumnCount; i++)
                {
                    serverRowAsString.Append(conflictingServerChange.Rows[0][i] + " | ");
                }
            }
            if (conflictingClientChange != null)
            {
                int clientColumnCount = conflictingClientChange.Columns.Count;
                for (int i = 0; i < clientColumnCount; i++)
                {
                    clientRowAsString.Append(conflictingClientChange.Rows[0][i] + " | ");
                }
            }
            Log.Error("CONFLICT DETECTED FOR SESSION " + e.Session.SessionId);
            Log.Error("** Client change **");
            Log.Error(clientRowAsString.ToString());
            Log.Error("** Server change **");
            Log.Error(serverRowAsString.ToString());
           
            //显示错误信息
            Log.Error(e.Error);
            
        }

        private static void ListTable()
        {
            Log.Info("ListTable Started");
            FileStream fs = new FileStream("./TableList.txt", FileMode.Open, FileAccess.Read);
            TableList = new List<string>();
            StreamReader sr = new StreamReader(fs);
            //使用StreamReader类来读取文件 
            sr.BaseStream.Seek(0, SeekOrigin.Begin);
            // 从数据流中读取每一行，直到文件的最后一行
            string tmp = sr.ReadLine();
            while (!string.IsNullOrEmpty(tmp))
            {
                TableList.Add(tmp);
                Log.Info("TableList+:" + tmp);
                tmp = sr.ReadLine();
            }
            //关闭此StreamReader对象 
            sr.Close();
            fs.Close();
            Log.Info("ListTable End");
        }

        private static void InitLog()
        {
            XmlConfigurator.Configure(new System.IO.FileInfo("log4net.config"));
            Log = LogManager.GetLogger(typeof(Program));
        }
    }
}
